#include "ui_application.h"
#include "ui_error.h"
#include <iostream>
#include <fstream>


namespace UI
{

UI_Application* UI_Application::m_pInstance = NULL;


void UI_Application::HandleSDLEvent(SDL_Event Event)
{

	switch (Event.type)
	{
	case SDL_VIDEORESIZE:
		UI_MessageServer::Instance().QueueMessage(new TPointMessage(
			UI_Message::CTRL_RESIZE, 0, this, UI_Point(Event.resize.w, Event.resize.h)));
		break;
	case SDL_KEYDOWN:
		UI_MessageServer::Instance().QueueMessage(new UI_KeyboardMessage(
			UI_Message::KEYBOARD_KEYDOWN, UI_Application::Instance()->GetKeyFocus(), this,
			Event.key.keysym.scancode, Event.key.keysym.mod,
			Event.key.keysym.sym, Event.key.keysym.unicode));
		break;
	case SDL_KEYUP:
		UI_MessageServer::Instance().QueueMessage(new UI_KeyboardMessage(
			UI_Message::KEYBOARD_KEYUP, UI_Application::Instance()->GetKeyFocus(), this,
			Event.key.keysym.scancode, Event.key.keysym.mod,
			Event.key.keysym.sym, Event.key.keysym.unicode));
		break;
	case SDL_MOUSEBUTTONDOWN:
		UI_MessageServer::Instance().QueueMessage(new UI_MouseMessage(
			UI_Message::MOUSE_BUTTONDOWN, UI_Application::Instance()->GetMouseFocus(), this,
			UI_Point(Event.button.x, Event.button.y), UI_Point(),
			UI_MouseMessage::TranslateSDLButton(Event.button.button)));
		break;
	case SDL_MOUSEBUTTONUP:
		UI_MessageServer::Instance().QueueMessage(new UI_MouseMessage(
			UI_Message::MOUSE_BUTTONUP, UI_Application::Instance()->GetMouseFocus(), this,
			UI_Point(Event.button.x, Event.button.y), UI_Point(),
			UI_MouseMessage::TranslateSDLButton(Event.button.button)));
		break;
	case SDL_MOUSEMOTION:
		UI_MessageServer::Instance().QueueMessage(new UI_MouseMessage(
			UI_Message::MOUSE_MOVE, UI_Application::Instance()->GetMouseFocus(), this,
			UI_Point(Event.motion.x, Event.motion.y), UI_Point(Event.motion.xrel, Event.motion.yrel),
			UI_MouseMessage::TranslateSDLButtonState(Event.motion.state)));
		break;
	case SDL_QUIT:
		UI_MessageServer::Instance().QueueMessage(new UI_Message(UI_Message::APP_EXIT, 0, this));
		break;
	default:
		UI_MessageServer::Instance().QueueMessage(new UI_SDLMessage(UI_Message::SDL, 0, this, Event));
		break;
	}
}

UI_Application::UI_Application() :
	m_sFontFileName("simfang.ttf"),
	m_sFontPath("./"),
	m_iExitCode(EXIT_FAILURE),
	m_bRunning(false),
	m_bInited(false),
	m_pKeyFocusWindow(NULL),
	m_pMouseFocusWindow(NULL),
	app_screen_bpp(DEFAULT_BPP),
	app_screen_width(DEFAULT_WIDTH),
	app_screen_height(DEFAULT_HEIGHT),
	m_default_font_color(COLOR_WHITE),
	m_default_font_size(14),
	m_default_font_style(0),
	m_pScreenSurface(NULL),
	m_pDraw(NULL)
{
	if (m_pInstance)
	{
		throw(UI_Ex_App("CApplication::CApplication : An instance of the CApplication already exists."));
	}

	m_pInstance = this;

	if(SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ) == -1)
	{
		throw(UI_Ex_SDL("CApplication::CApplication : Could not initialize SDL: " + std::string(SDL_GetError())));
	}

	if(TTF_Init() == -1)
	{
		throw(UI_Ex_SDL("CApplication::CApplication : Could not initialize SDL_ttf: " + std::string(TTF_GetError())));
	}

	if(SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL) == -1)
	{
		throw(UI_Ex_SDL("CApplication::Capplication : Error setting SDL keyboard repeat rate."));
	}

	m_AppLog.AddLogEntry("wGui Application starting", APP_LOG_INFO);

	atexit(SDL_Quit);
}


UI_Application::~UI_Application(void)
{
	if (m_pInstance == this)
	{
		m_pInstance = NULL;
	}
	m_AppLog.AddLogEntry("wGui Application closing", APP_LOG_INFO);
	m_AppLog.WriteToFile("wGui.log", false, std::string("wGui Application Log  \nSeverity Levels : [1] Critical, [3] Error, [5] Warning, [8] Info"));
}


void UI_Application::SetKeyFocus(UI_Window* pWindow)
{
	if (m_pKeyFocusWindow != pWindow)
	{
		if (m_pKeyFocusWindow)
		{
			UI_MessageServer::Instance().QueueMessage(new UI_Message(UI_Message::CTRL_LOSINGKEYFOCUS, m_pKeyFocusWindow, this));
		}
		m_pKeyFocusWindow = pWindow;
		UI_MessageServer::Instance().QueueMessage(new UI_Message(UI_Message::CTRL_GAININGKEYFOCUS, m_pKeyFocusWindow, this));
	}
}


void UI_Application::SetMouseFocus(UI_Window* pWindow)
{
	if (m_pMouseFocusWindow != pWindow)
	{
		if (m_pMouseFocusWindow)
		{
			UI_MessageServer::Instance().QueueMessage(new UI_Message(UI_Message::CTRL_LOSINGMOUSEFOCUS, m_pMouseFocusWindow, this));
		}
		m_pMouseFocusWindow = pWindow;
		UI_MessageServer::Instance().QueueMessage(new UI_Message(UI_Message::CTRL_GAININGMOUSEFOCUS, m_pMouseFocusWindow, this));
	}
}



void UI_Application::Init(void)
{
	UI_MessageServer::Instance().RegisterMessageClient(this, UI_Message::APP_EXIT, UI_MessageServer::PRIORITY_LAST);
	SDL_EnableUNICODE(1);

	bool bConfigLoaded = false;
	if (m_GlobalConfig.ReadFromFile("data/config/wgui.conf"))
	{
		m_AppLog.AddLogEntry("wGui Application config read from Local directory.", APP_LOG_INFO);
		bConfigLoaded = true;
	}

	if (!bConfigLoaded)
	{
		m_AppLog.AddLogEntry("wGui Application config was not found (see Global Config section of the docs).", APP_LOG_WARNING);
	}

	if(m_GlobalConfig.EntryExists("SCREEN_BPP"))
	{
		app_screen_bpp = m_GlobalConfig.GetLongIntEntry("SCREEN_BPP").second;
	}

	if(m_GlobalConfig.EntryExists("SCREEN_WIDTH"))
	{
		app_screen_width = m_GlobalConfig.GetLongIntEntry("SCREEN_WIDTH").second;
	}

	if(m_GlobalConfig.EntryExists("SCREEN_HEIGHT"))
	{
		app_screen_height = m_GlobalConfig.GetLongIntEntry("SCREEN_HEIGHT").second;
	}

	if(m_GlobalConfig.EntryExists("DEFAULTFONT"))
	{
		m_sFontFileName = m_GlobalConfig.GetStringEntry("DEFAULTFONT").second;
	}

	if(m_GlobalConfig.EntryExists("FONTPATH"))
	{
		m_sFontPath = m_GlobalConfig.GetStringEntry("FONTPATH").second;
	}

	if (m_GlobalConfig.EntryExists("DEFAULTTEXTCOLOR"))
	{
		m_default_font_color = UI_Color(m_GlobalConfig.GetStringEntry("DEFAULTTEXTCOLOR").second);
	}

	if (m_GlobalConfig.EntryExists("DEFAULTFONTSIZE"))
	{
		m_default_font_size = m_GlobalConfig.GetLongIntEntry("DEFAULTFONTSIZE").second;
	}

	if (m_GlobalConfig.EntryExists("DEFAULTTEXTSTYLE"))
	{
		m_default_font_style = m_GlobalConfig.GetLongIntEntry("DEFAULTTEXTSTYLE").second;
	}

	m_pScreenSurface = SDL_SetVideoMode(app_screen_width, app_screen_height, app_screen_bpp, SDL_SWSURFACE | SDL_FULLSCREEN);
	if (m_pScreenSurface == NULL)
		throw(UI_Ex_SDL(std::string("Could not set video mode : ") + std::string(SDL_GetError())) );

	m_pDraw = KTV_Draw::Create( m_sFontPath + m_sFontFileName);
	if(!m_pDraw->Initial()){
		throw(UI_Ex_SDL(std::string("Could not initial ttf : ")));
	}

	m_bInited = true;
}


void UI_Application::Exec(void)
{
	try
	{
		if (!m_bInited)
		{
			throw(UI_Ex_App("CApplication::Exec : Application Init() was not called!"));
		}

		m_bRunning = true;
		SDL_Event event;
		UI_MessageServer::Instance().IgnoreAllNewMessages(false);
		UI_MessageServer::Instance().QueueMessage(new UI_Message(UI_Message::APP_PAINT, 0, this));
		m_AppLog.AddLogEntry("wGui Application entering Exec loop", APP_LOG_INFO);
		while (m_bRunning)
		{
			while (SDL_PollEvent(&event))
			{
				HandleSDLEvent(event);
			}
			while (!UI_MessageServer::Instance().MessageAvailable())
			{
				while (SDL_PollEvent(&event))
				{
					HandleSDLEvent(event);
					SDL_Delay(5);
				}
				SDL_Delay(5);
			}
			try
			{
				UI_MessageServer::Instance().DeliverMessage();
			}
			catch (UI_Ex_Base& e)
			{
				m_AppLog.AddLogEntry("Exception (wGui) : " + e.std_what(), APP_LOG_CRITICAL);
				std::cerr<<("wGui exception while delivering message : " + e.std_what());
			}
		}
	}
	catch (UI_Ex_Base& e)
	{
		m_AppLog.AddLogEntry("Exception (wGui) : " + e.std_what(), APP_LOG_CRITICAL);
		m_AppLog.AddLogEntry("SDL Last Error = " + std::string(SDL_GetError()), APP_LOG_ERROR);
		std::cerr<<("Unhandled wGui exception : " + e.std_what());
	}
	catch (std::exception& e)
	{
		m_AppLog.AddLogEntry("Exception (std) : " + std::string(e.what()), APP_LOG_CRITICAL);
		m_AppLog.AddLogEntry("SDL Last Error = " + std::string(SDL_GetError()), APP_LOG_ERROR);
		std::cerr<<("Unhandled std exception : " + std::string(e.what()));
	}
	catch (...)
	{
		m_AppLog.AddLogEntry("Exception (non std)", APP_LOG_CRITICAL);
		m_AppLog.AddLogEntry("SDL Last Error = " + std::string(SDL_GetError()), APP_LOG_ERROR);
		std::cerr<<("Unhandled exception") << std::endl;
	}
}


void UI_Application::ApplicationExit(int iExitCode)
{
	/***********************************
	 *  ugly code here.
	 * ********/
	if(this->m_pDraw) delete this->m_pDraw;
	TTF_Quit();
	/*****************************************/
	SDL_Event user_event;
	user_event.type=SDL_USEREVENT;
	user_event.user.code=0;
	user_event.user.data1=0;
	user_event.user.data2=0;
	int iResult = SDL_PushEvent(&user_event);
	if(iResult == -1) std::cerr<<"CApplication::ApplicationExit - Unable to push SDL user_event.";
	m_iExitCode = iExitCode;
	m_bRunning = false;
}

bool UI_Application::HandleMessage(UI_Message* pMessage)
{
	bool bHandled = false;

	if (pMessage)
	{
		switch (pMessage->MessageType())
		{
		case UI_Message::APP_EXIT :
			ApplicationExit();
			bHandled = true;
			break;
		default:
			break;
		}
	}

	return bHandled;
}

}
